package com.bookmall.search.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.bookmall.common.constant.EsConstants;
import com.bookmall.common.entity.es.EsModel;
import com.bookmall.common.entity.to.MemberInfoTo;
import com.bookmall.search.config.ElasticSearchConfig;
import com.bookmall.search.domain.SearchParam;
import com.bookmall.search.domain.SearchResult;
import com.bookmall.search.domain.vo.BookCategoryVo;
import com.bookmall.search.feign.ProductFeignService;
import com.bookmall.search.service.IElasticSearchService;
import com.ruoyi.common.core.utils.StringUtils;
import com.ruoyi.common.core.web.domain.AjaxResult;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.bulk.BulkRequest;
import org.elasticsearch.action.bulk.BulkResponse;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.aggregations.bucket.terms.TermsAggregationBuilder;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description:
 * @Author 胡菜鸡
 * @Create 2022-04-08-15:55
 */
@Slf4j
@Service
public class ElasticSearchServiceImpl implements IElasticSearchService {
    @Autowired
    private RestHighLevelClient restHighLevelClient;

    @Autowired
    private ProductFeignService productFeignService;

    /**
     * 书籍上架
     * @author 胡菜鸡
     * @date 2022/4/8 15:57
     */
    @Override
    public boolean bookStatusUp(List<EsModel> esModels) throws IOException {
        //1、es中建立一个索引

        //2、给es中保存这些数据
        // 2.1 创建一个批量请求
        BulkRequest bulkRequest = new BulkRequest();
        for (EsModel esModel : esModels) {
            // 2.2 构造单条保存请求
            IndexRequest indexRequest = new IndexRequest(EsConstants.BOOK_INDEX);
            indexRequest.id(esModel.getId());
            // 以 JSON 形式保存数据
            indexRequest.source(JSON.toJSONString(esModel), XContentType.JSON);
            // 将数据添加到批量请求中
            bulkRequest.add(indexRequest);
        }
        // 2.3 执行批量请求操作
        BulkResponse bulk = restHighLevelClient.bulk(bulkRequest, ElasticSearchConfig.COMMON_OPTIONS);

        //3、打印上架结果
        List<String> failIds = Arrays.stream(bulk.getItems())
                .map(item -> item.getId())
                .collect(Collectors.toList());
        log.info("商品上架完成：{}，商品数据：{}", failIds, bulk.toString());

        return !bulk.hasFailures();
    }

    /**
     * 检索方法
     * @author 胡菜鸡
     * @date 2022/4/10 19:54
     */
    @Override
    public SearchResult search(SearchParam param) {
        //1、根据传递来的页面参数，去es中检索商品
        SearchResult result = null;

        //1、准备检索请求
        SearchRequest searchRequest = buildSearchRequest(param);
        try {
            //2、执行检索请求
            SearchResponse response = restHighLevelClient.search(searchRequest, ElasticSearchConfig.COMMON_OPTIONS);

            //3、分析响应数据，封装成需要的格式
            result = buildSearchResult(param, response);
        } catch (IOException e) {
            e.printStackTrace();
        }

        return result;
    }

    /**
     * 构建检索请求
     * @author 胡菜鸡
     * @date 2022/4/11 8:59
     */
    private SearchRequest buildSearchRequest(SearchParam param) {
        //用于构建DSL语句
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();

        /*
            模糊匹配，过滤
         */
        //1、构建bool-query
        BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
        // 1.1 must的模糊匹配
        if(!StringUtils.isBlank(param.getKeyword())){
            boolQuery.must(QueryBuilders.matchQuery("bookName", param.getKeyword()));
        }
        // 1.2 分类过滤（按照分类id、库存过滤）
        if(!StringUtils.isBlank(param.getCategoryId())){
            boolQuery.filter(QueryBuilders.termsQuery("bookType", param.getCategoryId()));
        }
        // 1.3 省份过滤
        if(!StringUtils.isBlank(param.getProvince())){
            boolQuery.filter(QueryBuilders.termsQuery("province", param.getProvince()));
        }
        // 1.4 学校过滤
        if(!StringUtils.isBlank(param.getUniversity())){
            boolQuery.filter(QueryBuilders.termsQuery("university", param.getUniversity()));
        }
        // 过滤掉下架的书籍
        boolQuery.filter(QueryBuilders.termsQuery("status", "0"));
        boolQuery.filter(QueryBuilders.termsQuery("hasStock", param.getHasStock() != 0));
        // 1.5 价格区间（$30 - $500）
        if(!StringUtils.isBlank(param.getBookPrice())){
            RangeQueryBuilder rangeQuery = QueryBuilders.rangeQuery("bookPrice");
            // 将价格区间分隔
            String[] priceRange = param.getBookPrice().split(" - ");
            // priceRange[0] = $30 | priceRange[1] = $500
            BigDecimal minPrice = new BigDecimal(priceRange[0].substring(1));
            BigDecimal maxPrice = new BigDecimal(priceRange[1].substring(1));
            rangeQuery.gte(minPrice).lte(maxPrice);
            boolQuery.filter(rangeQuery);
        }
        sourceBuilder.query(boolQuery);

        /*
            排序，分页，高亮
         */
        // 2.1 排序
        if(!StringUtils.isBlank(param.getSort())){
            String[] sort = param.getSort().split("_");
            SortOrder order = sort[1].equalsIgnoreCase("asc") ? SortOrder.ASC : SortOrder.DESC;
            sourceBuilder.sort(sort[0], order);
        }
        // 2.2 分页
        sourceBuilder.from((param.getPageNum()-1)*param.getPageSize());
        sourceBuilder.size(param.getPageSize());

        // 2.3 高亮
        if(!StringUtils.isBlank(param.getKeyword())){
            HighlightBuilder highlightBuilder = new HighlightBuilder();
            highlightBuilder.field("bookName");
            highlightBuilder.preTags("<b style='color: red'>");
            highlightBuilder.postTags("</b>");
            sourceBuilder.highlighter(highlightBuilder);
        }

        /*
            聚合分析
         */
        // 3.1 分类聚合
        // 构建分类聚合
        TermsAggregationBuilder categoryAgg = AggregationBuilders.terms("category_agg").field("bookType").size(20);
        // 构建其子聚合
        categoryAgg.subAggregation(AggregationBuilders.terms("category_name_agg").field("bookTypeName").size(1));
        // 添加分类聚合
        sourceBuilder.aggregation(categoryAgg);

        // 3.2 省份聚合
        // 构建省份聚合
        TermsAggregationBuilder provinceAgg = AggregationBuilders.terms("province_agg").field("province").size(10);
        sourceBuilder.aggregation(provinceAgg);

        // 3.3 学校聚合
        TermsAggregationBuilder universityAgg = AggregationBuilders.terms("university_agg").field("university").size(10);
        sourceBuilder.aggregation(universityAgg);

        String s = sourceBuilder.toString();
        System.out.println("构建的DSL：" + s);

        return new SearchRequest(new String[]{EsConstants.BOOK_INDEX}, sourceBuilder);
    }

    /**
     * 将数据封装成需要的格式（构建结果数据）
     * @author 胡菜鸡
     * @date 2022/4/11 9:00
     */
    private SearchResult buildSearchResult(SearchParam param, SearchResponse response) {
        SearchResult result = new SearchResult();
        //1、返回的所有查询到的书籍
        SearchHits hits = response.getHits();
        List<EsModel> esModelList = new ArrayList<>();
        if(hits.getHits() != null && hits.getHits().length > 0){
            for (SearchHit hit : hits.getHits()) {
                EsModel esModel = JSON.parseObject(hit.getSourceAsString(), EsModel.class);
                //设置高亮属性
                if(!StringUtils.isBlank(param.getKeyword())){
                    HighlightField bookName = hit.getHighlightFields().get("bookName");
                    esModel.setBookName(bookName.getFragments()[0].string());
                }
                esModelList.add(esModel);
            }
        }
        result.setBooks(esModelList);

        //2、书籍涉及的所有分类
        List<SearchResult.CategoryVo> categoryList = new ArrayList<>();
        // 获取分类聚合
        ParsedStringTerms categoryAgg = response.getAggregations().get("category_agg");
        // 依次遍历聚合内容
        for (Terms.Bucket bucket : categoryAgg.getBuckets()) {
            SearchResult.CategoryVo categoryVo = new SearchResult.CategoryVo();
            categoryVo.setCategoryId(bucket.getKeyAsString());
            // 获取分类聚合的子聚合 -> 分类名称聚合
            ParsedStringTerms categoryNameAgg = bucket.getAggregations().get("category_name_agg");
            // 获取分类名称
            String categoryName = categoryNameAgg.getBuckets().get(0).getKeyAsString();
            categoryVo.setCatName(categoryName);

            categoryList.add(categoryVo);
        }
        result.setCategoryList(categoryList);

        //3、书籍发布者的所有省份
        ParsedStringTerms provinceAgg = response.getAggregations().get("province_agg");
        List<String> provinceList = new ArrayList<>();
        for (Terms.Bucket bucket : provinceAgg.getBuckets()) {
            provinceList.add(bucket.getKeyAsString());
        }
        result.setProvinceList(provinceList);

        //4、书籍发布者的所有学校
        ParsedStringTerms universityAgg = response.getAggregations().get("university_agg");
        List<String> universityList = new ArrayList<>();
        for (Terms.Bucket bucket : universityAgg.getBuckets()) {
            universityList.add(bucket.getKeyAsString());
        }
        result.setUniversityList(universityList);

        //5、分页信息（当前页、总页码、总记录数等）
        result.setPageNum(param.getPageNum());
        result.setPageSize(param.getPageSize());
        long total = hits.getTotalHits().value;
        result.setTotal(total);
        long totalPage = total%param.getPageSize() == 0 ? total/param.getPageSize() : total/param.getPageSize()+1;
        result.setTotalPage(totalPage);
        if(!StringUtils.isBlank(param.getBookPrice())){
            // 将价格区间返回
            String[] rangePrice = param.getBookPrice().split(" - ");
            result.setMinPrice(Integer.parseInt(rangePrice[0].substring(1)));
            result.setMaxPrice(Integer.parseInt(rangePrice[1].substring(1)));
        }
        if(!StringUtils.isBlank(param.getCategoryId())){
            AjaxResult info = productFeignService.getInfo(param.getCategoryId());
            BookCategoryVo data = info.getData("data", new TypeReference<BookCategoryVo>() {
            });
            if(data == null){
                result.setSelectCategory("0");
                result.setSelectCategoryName("全部分类");
            } else {
                result.setSelectCategory(param.getCategoryId());
                result.setSelectCategoryName(data.getCatName());
            }
        }
        return result;
    }
}
